﻿Imports System.ComponentModel
Imports System.Reflection
Imports System.ComponentModel.Design.Serialization
Imports System

Public Class NumericValueTypeConverter
   Inherits TypeConverter

   Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, _
                                          ByVal destinationType As System.Type) _
                                          As Boolean
      Select Case Type.GetTypeCode(destinationType)
         Case TypeCode.Byte, TypeCode.SByte, _
              TypeCode.Double, TypeCode.Single, TypeCode.Decimal, _
              TypeCode.Int16, TypeCode.Int32, TypeCode.Int32, TypeCode.Int64, _
              TypeCode.UInt16, TypeCode.UInt32, TypeCode.UInt64, _
              TypeCode.String
            ' there should be no need to convert to a DBNull
            Return True
      End Select
      Return MyBase.CanConvertTo(context, destinationType)
   End Function


   Public Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, _
                                            ByVal sourceType As System.Type) _
                                            As Boolean

      Select Case Type.GetTypeCode(sourceType)
         Case TypeCode.Byte, TypeCode.SByte, _
              TypeCode.Double, TypeCode.Single, TypeCode.Decimal, _
              TypeCode.Int16, TypeCode.Int32, TypeCode.Int32, TypeCode.Int64, _
              TypeCode.UInt16, TypeCode.UInt32, TypeCode.UInt64, _
              TypeCode.String, _
              TypeCode.DBNull

              Return True

      End Select

      Return MyBase.CanConvertFrom(context, sourceType)
   End Function

   Public Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, _
                                       ByVal culture As System.Globalization.CultureInfo, _
                                       ByVal value As Object, _
                                       ByVal destinationType As System.Type) _
                                       As Object

      Select Case Type.GetTypeCode(destinationType)

         Case TypeCode.Byte
            Return CType(value, Byte)

         Case TypeCode.SByte
            Return CType(value, SByte)

         Case TypeCode.Int16
            Return CType(value, Int16)

         Case TypeCode.Int32
            Return CType(value, Int32)

         Case TypeCode.Int64
            Return CType(value, Int64)

         Case TypeCode.UInt16
            Return CType(value, UInt16)

         Case TypeCode.UInt32
            Return CType(value, UInt32)

         Case TypeCode.UInt64
            Return CType(value, UInt64)

         Case TypeCode.Double
            Return CType(value, Double)

         Case TypeCode.Decimal
            Return CType(value, Decimal)

         Case TypeCode.Single
            Return CType(value, Single)

         Case TypeCode.String
            Return value.ToString()

         Case Else
            Return MyBase.ConvertTo(context, culture, value, destinationType)

      End Select

   End Function


   Public Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, _
                                         ByVal culture As System.Globalization.CultureInfo, _
                                         ByVal value As Object) As Object

      Select Case Type.GetTypeCode(value.GetType)

         Case TypeCode.Byte, TypeCode.SByte, _
              TypeCode.Double, TypeCode.Single, TypeCode.Decimal, _
              TypeCode.Int16, TypeCode.Int32, TypeCode.Int32, TypeCode.Int64, _
              TypeCode.UInt16, TypeCode.UInt32, TypeCode.UInt64

              Return value

         Case TypeCode.String
            Dim s As String = CStr(value)
            ' all numeric types can be converted to Double
            Dim _Double As Double
            Dim passdbl As Boolean
            Try
               _Double = Double.Parse(s, culture)
               passdbl = True
            Catch ex As Exception
               passdbl = False
            End Try

            If passdbl Then 'have a numeric string
               ' determine if it may be an integer
               Dim IsInteger As Boolean = (Math.IEEERemainder(_Double, 1) = 0)
               If IsInteger Then
                  If Math.Abs(_Double) = _Double Then 'Positive value
                     ' try convert to UInt64
                     Try
                        Return UInt64.Parse(s, Globalization.NumberStyles.Any, culture.NumberFormat)
                     Catch ex As Exception
                     End Try

                  Else ' Negative, try Int64
                     Try
                        Return Int64.Parse(s, culture)
                     Catch ex As Exception
                     End Try

                  End If 'Math.Abs(dbl) = dbl

               Else ' has fractional component
                  ' see if can be cast as a Decimal to retain accuracy
                  Try
                     Return Decimal.Parse(s, Globalization.NumberStyles.Any, culture.NumberFormat)
                  Catch ex As Exception
                  End Try
                  Return _Double
               End If 'IsInteger
            Else 'Not Numeric
               'treat nonnumeric as zero
               Return 0US
               'If String.IsNullOrEmpty(s) Then 'treat empty as zero
               '   Return 0US
               'Else
               '   Return DBNull.Value
               'End If
            End If 'passdbl

         Case TypeCode.DBNull
            ' Null will be converted to zero
            Return 0US

      End Select

      Return MyBase.ConvertFrom(context, culture, value)
   End Function

End Class